/*
 * OREKIT-X
 * Copyright 2002-2008 CS Communication & Systemes
 * 
 * Parts of this software package have been licensed to CS
 * Communication & Systemes (CS) under one or more contributor license
 * agreements.  See the NOTICE file distributed with this work for
 * additional information.
 *  
 * This is an experimental copy of OREKIT from www.orekit.org.
 * Please use the original OREKIT from orekit.org for normal work
 * unrelated to this research project.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You
 * may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied.  See the License for the specific language governing
 * permissions and limitations under the License.
 */
package ore.propagation.events;

import ore.Attitude;
import ore.AttitudeLaw;
import ore.Frame;
import ore.SpacecraftState;
import ore.errors.OrekitException;
import ore.orbits.EquinoctialOrbit;
import ore.time.AbsoluteDate;

import org.apache.commons.math.ode.events.EventException;
import org.apache.commons.math.ode.events.EventHandler;

/** 
 * Adapt an {@link ore.propagation.events.EventDetector}
 * to commons-math {@link org.apache.commons.math.ode.events.EventHandler} interface.
 * 
 * @author Fabien Maussion
 */
public class AdaptedEventDetector
    implements EventHandler
{

    /** 
     * Underlying event detector.
     */
    private final EventDetector detector;
    /** 
     * Reference date from which t is counted.
     */
    private final AbsoluteDate referenceDate;
    /** 
     * Central body attraction coefficient (m<sup>3</sup>/s<sup>2</sup>).
     */
    private final double mu;
    /** 
     * Frame in which integration is performed.
     */
    private final Frame integrationFrame;
    /** 
     * Spacecraft attitude law.
     */
    private final AttitudeLaw attitudeLaw;


    /** 
     * @param detector Event detector to wrap
     * @param referenceDate Reference date from which t is counted
     * @param mu Central body attraction coefficient (m<sup>3</sup>/s<sup>2</sup>)
     * @param integrationFrame Frame in which integration is performed
     * @param attitudeLaw Spacecraft attitude law
     */
    public AdaptedEventDetector(EventDetector detector, AbsoluteDate referenceDate, double mu,
                                Frame integrationFrame, AttitudeLaw attitudeLaw)
    {
        super();
        this.detector         = detector;
        this.referenceDate    = referenceDate;
        this.mu               = mu;
        this.integrationFrame = integrationFrame;
        this.attitudeLaw      = attitudeLaw;
    }


    /**
     * {@inheritDoc}
     */
    public double g(double t, double[] y)
        throws EventException
    {
        try {
            return detector.g(mapState(t, y));
        }
        catch (OrekitException oe) {
            throw new EventException(oe);
        }
    }
    /**
     * {@inheritDoc}
     */
    public int eventOccurred(double t, double[] y, boolean increasing)
        throws EventException
    {
        try {
            final int whatNext = detector.eventOccurred(mapState(t, y), increasing);
            switch (whatNext) {
            case EventDetector.STOP :
                return STOP;
            case EventDetector.RESET_STATE :
                return RESET_STATE;
            case EventDetector.RESET_DERIVATIVES :
                return RESET_DERIVATIVES;
            default :
                return CONTINUE;
            }
        }
        catch (OrekitException oe) {
            throw new EventException(oe);
        }
    }
    /**
     * {@inheritDoc}
     */
    public void resetState(double t, double[] y)
        throws EventException
    {
        try {
            final SpacecraftState newState = detector.resetState(mapState(t, y));
            y[0] = newState.getA();
            y[1] = newState.getEquinoctialEx();
            y[2] = newState.getEquinoctialEy();
            y[3] = newState.getHx();
            y[4] = newState.getHy();
            y[5] = newState.getLv();
            y[6] = newState.getMass();
        }
        catch (OrekitException oe) {
            throw new EventException(oe);
        }
    }
    /** 
     * Convert state array to space dynamics objects
     * ({@link ore.time.AbsoluteDate AbsoluteDate} and
     * ({@link ore.orbits.Orbit OrbitalParameters}).
     * 
     * @param t Integration time (s)
     * @param y State as a flat array
     * 
     * @return State corresponding to the flat array as a space dynamics object
     * 
     * @exception OrekitException Attitude law cannot provide state
     */
    private SpacecraftState mapState( double t, double [] y)
        throws OrekitException
    {
        /*
         * State 
         */
        double a = y[0];
        double ex = y[1];
        double ey = y[2];
        double hx = y[3];
        double hy = y[4];
        double lv = y[5];
        double mass = y[6];
        /*
         * Update space dynamics view
         */
        AbsoluteDate currentDate = new AbsoluteDate(this.referenceDate, t);

        EquinoctialOrbit currentOrbit = new EquinoctialOrbit(a, ex, ey, hx, hy, lv,
                                                             EquinoctialOrbit.TRUE_LATITUDE_ARGUMENT,
                                                             this.integrationFrame, currentDate,
                                                             this.mu);

        Attitude currentAttitude = attitudeLaw.getState(currentDate,
                                                        currentOrbit.getPositionVelocity(),
                                                        integrationFrame);

        return new SpacecraftState(currentOrbit, currentAttitude, mass);
    }
}
